#include "dbusobjmodel_tree.hpp"
#include <QtDBus>
static QString headertitle[] ={"Bus","Service","Path","interface"};
DbusObjModel::DbusObjModel(QObject *parent) : QAbstractItemModel(parent)
{
    root = new QDBusItem(QDBusModel::PathItem,"/");
}

QDomDocument DbusObjModel::introspect(const QString &service,const QString &path,QDBusConnection c)
{
    QDomDocument doc;

    QDBusInterface iface(service, path, QLatin1String("org.freedesktop.DBus.Introspectable"), c);
    if (!iface.isValid()) {
        QDBusError err(iface.lastError());
        emit busError(QString::fromLatin1("Cannot introspect object %1 at %2:\n  %3 (%4)\n").arg(path).arg(
                      service).arg(err.name()).arg(err.message()));
        return doc;
    }

    QDBusReply<QString> xml = iface.call(QLatin1String("Introspect"));

    if (!xml.isValid()) {
        QDBusError err(xml.error());
        if (err.isValid()) {
            emit busError(QString::fromLatin1("Call to object %1 at %2:\n  %3 (%4) failed\n").arg(
                        path).arg(service).arg(err.name()).arg(err.message()));
        } else {
            emit busError(QString::fromLatin1("Invalid XML received from object %1 at %2\n").arg(
                    path).arg(service));
        }
        return doc;
    }

    doc.setContent(xml);
    return doc;
}

void DbusObjModel::addMethods(QDBusItem *parent, const QDomElement &iface)
{
    Q_ASSERT(parent);

    QDomElement child = iface.firstChildElement();
    while (!child.isNull()) {
        QDBusItem *item = 0;
        if (child.tagName() == QLatin1String("method")) {
            item = new QDBusItem(QDBusModel::MethodItem,
                    child.attribute(QLatin1String("name")), parent);
            item->caption = QLatin1String("Method: ") + item->name;
            //get "type" from <arg> where "direction" is "in"
            QDomElement n = child.firstChildElement();
            while (!n.isNull()) {
                if (n.attribute(QLatin1String("direction")) == QLatin1String("in"))
                {
                    item->typeSignature += n.attribute(QLatin1String("type"));
                    item->argTypes<<n.attribute(QLatin1String("type"));
                    item->argNames<<n.attribute(QLatin1String("name"));
                }

                n = n.nextSiblingElement();
            }
        } else if (child.tagName() == QLatin1String("signal")) {
            item = new QDBusItem(QDBusModel::SignalItem,
                    child.attribute(QLatin1String("name")), parent);
            item->caption = QLatin1String("Signal: ") + item->name;
        } else if (child.tagName() == QLatin1String("property")) {
            item = new QDBusItem(QDBusModel::PropertyItem,
                    child.attribute(QLatin1String("name")), parent);
            item->caption = QLatin1String("Property: ") + item->name;
        } else {
//            qDebug() << "addMethods: unknown tag:" << child.tagName();
        }
        if (item)
            parent->children.append(item);

        child = child.nextSiblingElement();
    }
}

void DbusObjModel::addPath(QDBusItem *parent,const QString &bus,const QString &service,const QString &path,const QString &interface)
{
    Q_ASSERT(parent);
    QDBusConnection c = getBus(bus);
    QDomDocument doc = introspect(service,path,c);
    QDomElement node = doc.documentElement();
    QDomElement child = node.firstChildElement();
    while (!child.isNull()) {
        if (child.tagName() == QLatin1String("interface")) {
            if(!interface.isEmpty ())
            {
                if(child.attribute ("name","") != interface)
                {
                    child = child.nextSiblingElement();
                    continue;
                }
            }
            QDBusItem *item = new QDBusItem(QDBusModel::InterfaceItem,
                                            child.attribute(QLatin1String("name")), parent);
            item->setDbusPath (path);
            parent->children.append(item);
            {
                addMethods(item, child);
            }

        } else {
//            qDebug() << "addPath: Unknown tag name:" << child.tagName();
        }
        child = child.nextSiblingElement();
    }

    parent->isPrefetched = true;
}
void DbusObjModel::append(const QString &bus, const QString &service, const QString &path, const QString &interface)
{
    QDBusItem *item = new QDBusItem(bus,service,path,interface,root);
    append (item);
}

void DbusObjModel::append(QList<QStringList> data)
{
//    m_data<<data;
}

QModelIndex DbusObjModel::index(int row, int column, const QModelIndex &parent) const
{
    const QDBusItem *item = static_cast<QDBusItem *>(parent.internalPointer());
    if (!item)
        item = root;

    if (column != 0 || row < 0 || row >= item->children.count())
        return QModelIndex();

    return createIndex(row, 0, item->children.at(row));
}

QModelIndex DbusObjModel::parent(const QModelIndex &child) const
{
    QDBusItem *item = static_cast<QDBusItem *>(child.internalPointer());
    if (!item || !item->parent || !item->parent->parent)
        return QModelIndex();

    return createIndex(item->parent->parent->children.indexOf(item->parent), 0, item->parent);
}

int DbusObjModel::rowCount(const QModelIndex &parent) const
{
    QDBusItem *item = static_cast<QDBusItem *>(parent.internalPointer());
    if (!item)
        item = root;
    return item->children.count();
}

int DbusObjModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}

QVariant DbusObjModel::data(const QModelIndex &index, int role) const
{
    const QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
    if (!item)
        return QVariant();

    if (role == Qt::UserRole+1)
        return item->getDbusPath ();
    if (role == Qt::UserRole+2)
        return item->getIsPath ();


    if (role != Qt::DisplayRole)
        return QVariant();

    return item->caption.isEmpty() ? item->name : item->caption;
}

QVariant DbusObjModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole || orientation == Qt::Vertical || section != 0)
        return QVariant();

    return QLatin1String("Methods");
}

QDBusModel::Type DbusObjModel::itemType(const QModelIndex &index) const
{
    const QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
    return item ? item->type : QDBusModel::PathItem;
}

Qt::ItemFlags DbusObjModel::flags(const QModelIndex &index) const
{
    return QAbstractItemModel::flags (index);
}
